#!/usr/bin/python
import cherrypy
import csv
import time
import datetime
import json
import math
import httplib

#Linhas
#route
#  Retorna a lista de linhas
#route/<numero_linha>
#  Retorna os pontos da linha
#route/<numero_linha>/<numero_ponto>
#  Retorna em quanto tempo o proximo onibus chegara
class Route(object):

    def __init__(self):
        self.content = None

    exposed = True

    @staticmethod
    def get_bus_position(route):
        conn = httplib.HTTPConnection("mc933.lab.ic.unicamp.br:8017")

        if (int(route) == 2):
            baseURI = '/onibus/0'
        elif (int(route) == 3):
            baseURI = '/onibus/1'
        else:
            baseURI = '/onibus/' + str(route)

        print("baseURI: " + baseURI)

        conn.request("GET", baseURI)
        r1 = conn.getresponse()
        data1 = r1.read()

        if (len(data1) <= 0):
            return None
        
        data1 = json.loads(data1)

        print("dados positron: " + str(data1))
        
        return data1

    @staticmethod
    def get_travel_time(checkpoints, init_long, init_lati, end_long, end_lati):
        total_time = 0

        #print("get_travel_time - checkpoints: " + str(checkpoints))
        
        conn = httplib.HTTPConnection("maps.googleapis.com")

        origin = str(init_lati) + ',' + str(init_long)
        destination = str(end_lati) + ',' + str(end_long)

        waypoints = "|"
        waypoints_count = 0

        for count in range(1,len(checkpoints)):
            total_time += Route.time_to_sec(checkpoints[count]["time"])
            if (len(checkpoints[count]["waypoint"]) > 0):
                waypoints_count += 1
                waypoints += checkpoints[count]["waypoint"] + "|"
            if (int(checkpoints[count]["id"]) == 123 or int(checkpoints[count]["id"]) == 135 or waypoints_count == 8):
                if (count + 1 < len(checkpoints)):
                    total_time += Route.get_travel_time(checkpoints[count + 1:len(checkpoints)], checkpoints[count+1]["long"], checkpoints[count+1]["lati"], end_long, end_lati)
                    destination = str(checkpoints[count]["lati"]) + ',' + str(checkpoints[count]["long"])
                    break

        baseURI = '/maps/api/directions/json?sensor=false&'
        baseURI += 'origin=' + origin + '&'
        baseURI += 'destination=' + destination + '&'
        if (waypoints_count > 0):
            baseURI += 'waypoints=' + waypoints

        print("get_travel_time - baseURI: " + baseURI)

        conn.request("GET", baseURI)
        r1 = conn.getresponse()
        data1 = r1.read()
        data1 = json.loads(data1)

        for entry in data1["routes"][0]["legs"]:
            total_time += entry["duration"]["value"]

        print("total_time: " + str(total_time))

        return total_time

    @staticmethod
    def closest_segment(route, checkpoints, bus_long, bus_lati):
        closest_checkpoints = None
        min_dist = None
        list_dist = []

        for count in range(1,len(checkpoints["checkpoints"])):
            if (float(checkpoints["checkpoints"][count-1]["long"]) == float(checkpoints["checkpoints"][count]["long"]) or
                float(checkpoints["checkpoints"][count-1]["lati"]) == float(checkpoints["checkpoints"][count]["lati"])):
                continue
            
            dist = Route.dist_line_point(float(checkpoints["checkpoints"][count-1]["long"]), float(checkpoints["checkpoints"][count-1]["lati"]),
                                        float(checkpoints["checkpoints"][count]["long"]),   float(checkpoints["checkpoints"][count]["lati"]),
                                        bus_long, bus_lati)

            list_dist.append(dist)

            if (min_dist == None or dist < min_dist):
                min_dist = dist
                closest_checkpoints = checkpoints["checkpoints"][count-1]

        print("closest_segment - min_dist: " + str(min_dist))

        #Posicao do onibus muito longe de qualquer segmento
        if (min_dist > 0.001):
            return None
        
        return closest_checkpoints
    
    @staticmethod
    def dist_line_point(x1,y1, x2,y2, x3,y3):
        px = x2-x1
        py = y2-y1

        s = px*px + py*py

        u =  ((x3 - x1) * px + (y3 - y1) * py) / s

        if u > 1:
            u = 1
        elif u < 0:
            u = 0

        x = x1 + u * px
        y = y1 + u * py

        dx = x - x3
        dy = y - y3

        dist = math.sqrt(dx*dx + dy*dy)

        return dist

    @staticmethod
    def dist_point_to_point(x1,y1, x2,y2):
        return math.sqrt(pow(float(x1) - float(x2),2) + pow(float(y1) - float(y2),2))

    @staticmethod
    def time_to_sec(time):
        timeParts = [int(s) for s in time.split(':')]
        return (timeParts[0] * 60 + timeParts[1]) * 60 + timeParts[2]

    @staticmethod
    def get_time_route_bus_stop(route, bus_stop):
        #coordenada do onibus
        bus_position = Route.get_bus_position(route)

        if (bus_position == None):
            return None
        
        bus_long = bus_position["longitude"]
        bus_lati = bus_position["latitude"]
        bus_server_time = bus_position["systemDatetime"]
        total_time = 0

        #TODO: testes
        #bus_long = -47.072629
        #bus_lati = -22.816299

        print("bus_long: " + str(bus_long))
        print("bus_lati: " + str(bus_lati))
        print("bus_server_time: " + str(bus_server_time[len(bus_server_time)-8:len(bus_server_time)]))

        FMT = '%Y-%m-%d %H:%M:%S'
        t_delta = datetime.datetime.now() - datetime.datetime.strptime(bus_server_time, FMT)

        delta_secs = (t_delta.microseconds + (t_delta.seconds + t_delta.days * 24 * 3600) * 10**6) / 10**6

        print("t_delta: " + str(t_delta))
        print("t_delta (seconds): " + str(delta_secs))

        if (delta_secs > 180):
            return ""

        total_time -= delta_secs

        #Carrega a lista de checkpoints
        stream = open('data/checkpoints' + route + '.yml')
        checkpoints = json.load(stream)

        #Carrega lista que relaciona os checkpoints com os pontos de onibus
        stream1 = open('data/segment_busstop' + route + '.yml')
        seg_stops = json.load(stream1)

        #Carrega a lista de pontos
        stream2 = open('data/pontos' + route + '.yml')
        bus_stops = json.load(stream2)

        #Encontra o ponto de onibus
        bus_stop_found = filter(lambda p: int(p["ID_BUS_STOP"]) == int(bus_stop), bus_stops["pontos"])
        print("bus_stop: " + str(bus_stop_found))

        #Encontra qual o checkpoint mais proximo do onibus
        closest_check = Route.closest_segment(route, checkpoints, bus_long, bus_lati);
        if (closest_check == None):
            return ""
            
        print("closest_check: " + str(closest_check))

        #Encontra em qual checkpoint esta o ponto desejado
        seg_stops = sorted(seg_stops["data"], key=lambda p: int(p["seq"]))
        segment_wanted = filter(lambda p: int(p["bus_stop"]) == int(bus_stop), seg_stops)

        print("segment_wanted: " + str(segment_wanted))

        #Filtra a lista de checkpoints mantendo apenas os checkpoints que estao entre o onibus e o ponto desejado
        if (int(segment_wanted[0]["checkpoint"]) > int(closest_check["id"])):
            checkpoints_filt = filter(lambda p: int(p["id"]) >= int(closest_check["id"]) and int(p["id"]) <= int(segment_wanted[0]["checkpoint"]), checkpoints["checkpoints"])
        else:
            checkpoints_filt = filter(lambda p: int(p["id"]) >= int(closest_check["id"]), checkpoints["checkpoints"])+ filter(lambda p: int(p["id"]) <= int(segment_wanted[0]["checkpoint"]), checkpoints["checkpoints"])

        #print("checkpoints_filt: " + str(checkpoints_filt))

        total_time = Route.get_travel_time(checkpoints_filt, bus_long, bus_lati, bus_stop_found[0]["Longitude"], bus_stop_found[0]["Latitude"])

        #Soma o tempo de todos os checkpoints
        #totalSecs = 0
        #for entry in checkpoints_filt:
        #    totalSecs += self.time_to_sec(entry["time"])

        #print("totalSecs: " + str(totalSecs))

        #Subtrai o tempo do primeiro segmento ate a posicao do onibus
        #checkpoints_init = filter(lambda p: int(p["id"]) == int(closest_check["id"]) or int(p["id"]) == int(closest_check["id"])+1, checkpoints["checkpoints"])
        #dist_to_bus = self.dist_point_to_point(bus_long, bus_lati, checkpoints_init[0]["long"], checkpoints_init[0]["lati"])
        #dist_segment = self.dist_point_to_point(checkpoints_init[0]["long"], checkpoints_init[0]["lati"], checkpoints_init[1]["long"], checkpoints_init[1]["lati"])
        #totalSecs -= self.time_to_sec(checkpoints_init[0]["time"]) * dist_to_bus/dist_segment

        #print("rebarba inicial: " + str(self.time_to_sec(checkpoints_init[0]["time"]) * dist_to_bus/dist_segment))

        #dist_to_bus = None
        #dist_segment = None

        #Subtrai o tempo do ultimo segmento ate a posicao do ponto de onibus
        #checkpoints_end = filter(lambda p: int(p["id"]) == int(segment_wanted[0]["checkpoint"]) or int(p["id"]) == int(segment_wanted[0]["checkpoint"])+1, checkpoints["checkpoints"])
        #dist_to_bus_stop = self.dist_point_to_point(bus_stop[0]["Longitude"], bus_stop[0]["Latitude"], checkpoints_end[1]["long"], checkpoints_end[1]["lati"])
        #dist_segment = self.dist_point_to_point(checkpoints_end[0]["long"], checkpoints_end[0]["lati"], checkpoints_end[1]["long"], checkpoints_end[1]["lati"])
        #totalSecs -= self.time_to_sec(checkpoints_end[0]["time"]) * dist_to_bus_stop/dist_segment

        #print("rebarba final: " + str(self.time_to_sec(checkpoints_end[0]["time"]) * dist_to_bus_stop/dist_segment))

        #Subtrai do tempo total o tempo que ja se passou desde a ultima atualizacao do gps do onibus
        #TODO

        #print("totalSecs (apos aparar rebarbas): " + str(totalSecs))

        return total_time

    @cherrypy.tools.json_out()
    def GET(self, *path, **args):
        print("path: " + str(path))
        try:
            if len(path) == 0:
                return "lista de linhas"

            if len(path) == 1:
                #return self.LastBusStop
                data = list(csv.reader(open('data/circular' + path[0] + '.csv')))
                return data

            elif len(path) == 2:
                return Route.get_time_route_bus_stop(path[0], path[1])
        except:
            pass
        return None


#Pontos
#bus_stop?lati=<latitude>&long=<longitude>
#  Retorna os pontos mais proximos de long/lati
#bus_stop/<numero_ponto>
#  Retorna as linhas que passam pelo ponto
#bus_stop/<numero_ponto>/<numero_ponto1>
#  Retorna as linhas que passam pelos pontos
class BusStop(object):

    def __init__(self):
        self.content = None

    exposed = True

    @cherrypy.tools.json_out()
    def GET(self, *path, **args):
        if len(path) == 0:
            user_long = args["long"]
            user_lati = args["lati"]

            stream = open('data/pontos1.yml')
            data = json.load(stream)

            stream = open('data/pontos2.yml')
            data = json.load(stream)

            sorted_list = sorted(data["pontos"], key=lambda p: math.sqrt(pow(float(user_long) - float(p["Longitude"]),2) + pow(float(user_lati) - float(p["Latitude"]),2)))
            filtered_list = filter(lambda p: math.sqrt(pow(float(user_long) - float(p["Longitude"]),2) + pow(float(user_lati) - float(p["Latitude"]),2) < 0.00001), data["pontos"])
            return sorted_list[0:5]
            return filtered_list

        if len(path) == 1:
            list_routes = []

            stream1 = open('data/pontos1.yml')
            data1 = json.load(stream1)

            stream2 = open('data/pontos2.yml')
            data2 = json.load(stream2)

            filtered_list1 = filter(lambda p: p["ID_BUS_STOP"] == path[0], data1["pontos"])
            if (len(filtered_list1)):
                list_routes.append({"route":"1", "time":str(Route.get_time_route_bus_stop("1", path[0]))})

            filtered_list2 = filter(lambda p: p["ID_BUS_STOP"] == path[0], data2["pontos"])
            if (len(filtered_list2)):
                list_routes.append({"route":"2", "time":str(Route.get_time_route_bus_stop("2", path[0]))})

            return list_routes

        elif len(path) == 2:
            return "linhas"
        
        return "{}"

class Root(object):
    pass

root = Root()

root.route = Route()
root.bus_stop = BusStop()

conf = {
    'global': {
        'server.socket_host': '0.0.0.0',
        'server.socket_port': 8009,
    },
    '/': {
        'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
    }
}

cherrypy.quickstart(root, '/', conf)
